/*
* Copyright (C) 2013 Sasha Vasko <sasha at aftercode dot net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.wifiafterconnect.html;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Comment;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import android.util.Log;
import com.wifiafterconnect.Constants;
import com.wifiafterconnect.util.HttpInput;
public class HtmlPage extends HttpInput {
private Map<String,HtmlForm> namedForms = new HashMap<String,HtmlForm>();
private List<HtmlForm> forms = new ArrayList<HtmlForm>();
private List<JavaScript> headJavaScripts = new ArrayList<JavaScript>();
private List<JavaScript> bodyJavaScripts = new ArrayList<JavaScript>();
private String onLoad = "";
private String title = "";
private WISPAccessGatewayParam wISPr = null;
private Map<String,String> namedMetas = new HashMap<String,String>();
private Map<String,String> httpEquivMetas = new HashMap<String,String>();
public class MetaRefresh {
private int timeout = 0;
private String url = null;
public MetaRefresh (String source) {
int start = source.toLowerCase(Locale.ENGLISH).indexOf("url=");
if (start >= 0)
url = source.substring(start+4);
if (start > 2) {
int end = source.indexOf(';');
if (end > 0)
timeout = Integer.parseInt(source.substring(0, end));
}
}
public int getTimeout () {
return timeout;
}
public String getURLString () {
return url == null ? "" : url;
}
public URL getURL () throws MalformedURLException {
return makeURL (url);
}
}
public HtmlPage (URL url){
super (url);
}
@Override
public String getTitle () {
return title;
}
protected boolean isJavaScript (Element jse){
if (!jse.tag().toString().equals("script"))
return false;
String attr = "";
if (jse.hasAttr("language"))
attr = jse.attr("language");
else if (jse.hasAttr("type"))
attr = jse.attr("type");
return attr.equalsIgnoreCase("javascript") || attr.equalsIgnoreCase("text/javascript");
}
@Override
public boolean parse (String html) {
Log.d(Constants.TAG, "Page " + this);
if (!super.parse(html))
return false;
Document doc = Jsoup.parse(html);
if (doc == null) {
Log.d(Constants.TAG, "Parsing html: doc == null");
return false;
}
Log.d(Constants.TAG, "Parsing html: doc html == {" + doc.html() + "}");
// some portals sneak form to outside of <div id="content"> - the bastards!
Element content = doc;/*.getElementById("content");
if (content == null) {
content = doc;
}*/
for (Element meta : content.getElementsByTag("meta")) {
String c = meta.attr("content");
if (!c.isEmpty()) {
if (meta.hasAttr("http-equiv"))
httpEquivMetas.put(meta.attr("http-equiv").toLowerCase(Locale.ENGLISH), c);
else if (meta.hasAttr("name"))
namedMetas.put(meta.attr("name").toLowerCase(Locale.ENGLISH), c);
}
}
for (Element te : content.getElementsByTag("title")) {
title = te.data();
if (!title.isEmpty())
break;
}
for (Element body : content.getElementsByTag("body")) {
Log.d(Constants.TAG, "Parsing html: body found.");
if (body.hasAttr("onLoad")) {
onLoad = body.attr("onLoad");
break;
}
}
for (Element fe : content.getElementsByTag("form")) {
HtmlForm f = new HtmlForm (fe);
forms.add (f);
Log.d(Constants.TAG, "Parsing html: form added. Forms == " + forms.toString());
String fid = f.getId();
if (!fid.isEmpty())
namedForms.put(fid, f);
}
for (Element head : content.getElementsByTag("head")) {
for (Element jse : head.getElementsByTag("script")) {
if (isJavaScript(jse)){
JavaScript j = new JavaScript (jse);
headJavaScripts.add (j);
Log.d(Constants.TAG, "Parsing html: HEAD JS added. javaScripts = " + headJavaScripts.toString());
}
}
if (!headJavaScripts.isEmpty())
checkJavaScriptForMetaRefresh();
}
for (Element body : content.getElementsByTag("body")) {
for (Element jse : body.getElementsByTag("script")) {
if (isJavaScript(jse)){
JavaScript j = new JavaScript (jse);
bodyJavaScripts.add (j);
Log.d(Constants.TAG, "Parsing html: HEAD JS added. javaScripts = " + bodyJavaScripts.toString());
}
}
}
for (Element ie : content.getElementsByTag("input")) {
HtmlInput i = new HtmlInput (ie, false);
String fid = i.getFormId();
if (!fid.isEmpty()) {
HtmlForm f = namedForms.get(fid);
if (f != null)
f.addInput (i);
}
}
for(Element e : doc.getAllElements()){
for(Node n: e.childNodes()){
if(n instanceof Comment){
String commentData = ((Comment)n).getData();
if (commentData.startsWith("<?xml")) {
WISPAccessGatewayParam wp = WISPAccessGatewayParam.parse(commentData);
if (wp != null)
wISPr = wp;
}
}
}
}
return true;
}
public JavaScript getHeadJavaScript (final String signature) {
for (JavaScript js : headJavaScripts)
if (js.matchCode(signature) >= 0)
return js;
return null;
}
private void checkJavaScriptForMetaRefresh() {
// This is specific to GuestNetInc portals :
final String signature = "document.write('<meta http-equiv=\"REFRESH\" content=\"0;url=' + cpUrl";
if (getHeadJavaScript (signature) != null) {
JavaScript js = getHeadJavaScript ("cpUrl =");
Log.d(Constants.TAG, "checkJavaScriptForMetaRefresh(): js for cpUrl = " + js);
if (js != null) {
String cpUrl = js.evalStringVar ("cpUrl");
Log.d(Constants.TAG, "checkJavaScriptForMetaRefresh(): cpUrl = " + cpUrl);
if (!cpUrl.isEmpty()){
String pAction = js.evalStringVar ("pAction");
String controllerType = js.evalStringVar ("ControllerType");
String hotelGroup = js.evalStringVar ("HotelGroup");
String hotelBrand = js.evalStringVar ("HotelBrand");
String hotelId = js.evalStringVar ("HotelId");
String usr = js.evalStringVar ("usr");
String pwd = js.evalStringVar ("pwd");
cpUrl += "pa=" + pAction;
if (!controllerType.isEmpty())
cpUrl += "&ct=" + controllerType;
if (!hotelGroup.isEmpty())
cpUrl += "&hg=" + hotelGroup;
if (!hotelBrand.isEmpty())
cpUrl += "&hb=" + hotelBrand;
if (!hotelId.isEmpty())
cpUrl += "&id=" + hotelId;
if (!usr.isEmpty())
cpUrl += "&usr=" + usr;
if (!pwd.isEmpty())
cpUrl += "&pwd=" + pwd;
httpEquivMetas.put("refresh", "0;url=" + cpUrl);
}
}
}
}
public String getOnLoad() {
return onLoad;
}
public WISPAccessGatewayParam getWISPr() {
return wISPr;
}
// For consistency sake both getForm methods return null if key is bad and don't throw an exception
public HtmlForm getForm (int id) {
HtmlForm f = null;
try {
f = forms.get(id);
}catch (IndexOutOfBoundsException e)
{
Log.d(Constants.TAG, "Page " + this + ". Index out of bounds retrieving the form #" + id + ". Forms == " + forms.toString());
}
return f;
}
public static HtmlForm getForm(HttpInput page, int id) {
return (page != null && (page instanceof HtmlPage)) ? ((HtmlPage)page).getForm(id) : null;
}
public HtmlForm getForm (String id) {
HtmlForm f = null;
if (id != null)
f = namedForms.get(id);
return f;
}
public static HtmlForm getForm(HttpInput page, String id) {
return (page != null && id != null && (page instanceof HtmlPage)) ? ((HtmlPage)page).getForm(id) : null;
}
public HtmlForm getForm () {
return getForm(0);
}
public static HtmlForm getForm(HttpInput page) {
return (page != null && (page instanceof HtmlPage)) ? ((HtmlPage)page).getForm() : null;
}
public Collection<HtmlForm> forms () {
return this.forms;
}
public boolean hasMetaRefresh() {
return httpEquivMetas.containsKey("refresh");
}
public MetaRefresh getMetaRefresh () {
String metaRefresh = httpEquivMetas.get ("refresh");
return (metaRefresh != null)? new MetaRefresh(metaRefresh) : null ;
}
public String getMeta (final String name) {
String c = namedMetas.get(name);
return (c == null) ? "" : namedMetas.get(name);
}
public String getDocumentReadyFunc () {
String func = null;
for (JavaScript js : headJavaScripts) {
if ((func = js.getDocumentReadyFunc()) != null)
return func;
}
return func;
}
public boolean hasFormWithInputType (String type) {
for (HtmlForm f : forms) {
if (f.getVisibleInputByType(type) != null)
return true;
}
return false;
}
public boolean hasSubmittableForm() {
for (HtmlForm f : forms) {
if (f.isSubmittable())
return true;
}
return false;
}
}